package com.gdhz.xiaohengqin.handler;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gdhz.xiaohengqin.device.constants.WieGandConstant;
import com.gdhz.xiaohengqin.device.model.DeviceStatus;
import com.gdhz.xiaohengqin.device.model.entity.AccessGate;
import com.gdhz.xiaohengqin.device.repository.AccessGateRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 处理闸机状态消息
 *
 * @author Swallows
 * @date 2025-05-30 16:02
 */
@Slf4j
@Component
public class DoorStatusHandler implements MessageHandler {

    private final ReactiveRedisTemplate<String, String> redisTemplate;
    private final AccessGateRepository accessGateRepository;
    private final ObjectMapper objectMapper;

    public DoorStatusHandler(ReactiveRedisTemplate<String, String> redisTemplate,
                             ObjectMapper objectMapper,
                             AccessGateRepository accessGateRepository) {
        this.redisTemplate = redisTemplate;
        this.accessGateRepository = accessGateRepository;
        this.objectMapper = objectMapper;
    }

    @Override
    public boolean canHandle(JsonNode message) {
        return message.get(0).has(WieGandConstant.MESSAGE_TYPE_STATUS);
    }

    @Override
    public Mono<Void> handle(JsonNode message) {
        // 检查 message 是否是一个数组
        if (!message.isArray()) {
            log.error("Invalid message format: expected an array");
            return Mono.empty();
        }

        // 遍历 message 并处理每个设备数据
        return Flux.fromIterable(message::elements) // 将 JsonNode 数组转换为 Flux 流
                .flatMap(element -> Mono.just(objectMapper.convertValue(element, DeviceStatus.class)))
                .flatMap(deviceStatus -> {
//                    1. 处理单个设备的状态，返回包含状态和SN的Map
                    Mono<Map<String, String>> statusMapMono = this.processDeviceStatus(deviceStatus);

//                    2. 并行执行数据库操作和为Redis收集数据
                    return statusMapMono.flatMap(statusMap -> {
//                        从map中获取状态
                        int status = Integer.parseInt(statusMap.get(String.valueOf(deviceStatus.getSn())));
//                        执行数据库保存/更新，并在此操作完成后继续传递statusMap
                        return this.saveOrUpdateAccessGate(deviceStatus, status).thenReturn(statusMap);
                    });
                })
                .collectList()                            // 收集所有设备的状态为 List<Map<String, String>>
                .flatMap(this::saveToRedis)// 将状态列表存储到 Redis
                .then();
    }

    private Mono<Map<String, String>> processDeviceStatus(DeviceStatus deviceData) {
        try {
            if (deviceData.getSn() != 0) {
                log.info("门禁状态信息进来了");
                // 计算时间差（秒）
                long secondsDifference = Duration.between(deviceData.getRefreshTime(), deviceData.getServerTime()).getSeconds();

                // 判断设备状态 0 在线 1离线
                int status = (secondsDifference > 60) ? 1 : 0;
                log.info("[{}] 设备状态: {}", deviceData.getSn(), status == 0 ? "在线" : "离线");

                // 返回 SN 和状态的映射
                Map<String,  String> statusMap = new HashMap<>();
                statusMap.put(String.valueOf(deviceData.getSn()), String.valueOf(status));
                return Mono.just(statusMap);
            }
            return Mono.empty();

        } catch (Exception e) {
            // 捕获异常并记录错误日志
            log.error("Error processing device data: {}", deviceData, e);
            return Mono.empty(); // 忽略错误并继续处理下一个设备
        }
    }

    /**
     * 根据SN保存或更新门禁设备信息到数据库，并更新刷新时间。
     *
     * @param deviceStatus 包含设备SN和刷新时间的对象
     * @param status       计算出的设备状态 (0:在线, 1:离线)
     * @return Mono<Void>
     */
    private Mono<Void> saveOrUpdateAccessGate(DeviceStatus deviceStatus, int status) {
        Integer sn = deviceStatus.getSn();

        // 根据sn查找设备，如果存在则更新，不存在则创建新设备
        return accessGateRepository.findById(sn)
                .flatMap(existingGate -> {
                    // 设备已存在，更新状态和刷新时间
                    existingGate.setStatus(status);
                    existingGate.setRefreshTime(deviceStatus.getRefreshTime());
                    return accessGateRepository.updateAccessGate(existingGate);
                })
                .switchIfEmpty(Mono.defer(() -> {
                    // 设备不存在，创建新记录
                    AccessGate newGate = new AccessGate();
                    newGate.setSn(sn);
                    newGate.setStatus(status);
                    newGate.setRefreshTime(deviceStatus.getRefreshTime());
                    return accessGateRepository.save(newGate);
                }))
                .then(); // 转换为 Mono<Void>
    }

    private Mono<Boolean> saveToRedis(List<Map<String, String>> statusList) {
        if (statusList == null || statusList.isEmpty()) {
            return Mono.empty();
        }
        // 合并所有设备的状态到一个 Map
        Map<String, String> allStatus = statusList.stream()
                .flatMap(map -> map.entrySet().stream())
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        // 存储到 Redis
        return redisTemplate.opsForHash().putAll(WieGandConstant.DEVICE_STATUS, allStatus)
                .then(redisTemplate.expire(WieGandConstant.DEVICE_STATUS, Duration.ofSeconds(40)));
    }
}
